/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.web.flux.handler;

import com.iwindplus.base.domain.exception.CommonException;
import com.iwindplus.base.domain.vo.ResultVO;
import com.iwindplus.base.util.CryptoUtil;
import com.iwindplus.base.util.ExceptionUtil;
import com.iwindplus.base.util.HttpsUtil;
import com.iwindplus.base.util.JacksonUtil;
import com.iwindplus.base.util.ReactorUtil;
import com.iwindplus.base.web.domain.property.GlobalErrorProperty;
import com.iwindplus.base.web.domain.property.ResponseBodyProperty;
import com.iwindplus.base.web.domain.property.ResponseBodyProperty.ResponseBodyCryptoConfig;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.annotation.Resource;
import java.net.URI;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

/**
 * reactor 端全局异常处理.
 *
 * @author zengdegui
 * @since 2024/06/19 23:08
 */
@Slf4j
@Hidden
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@EnableConfigurationProperties({GlobalErrorProperty.class, ResponseBodyProperty.class})
@ConditionalOnProperty(prefix = "global.error", name = "enabled", havingValue = "true", matchIfMissing = true)
public class GlobalErrorHandler implements WebExceptionHandler {

    @Resource
    private GlobalErrorProperty globalErrorProperty;

    @Resource
    private ResponseBodyProperty responseBodyProperty;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        final String realIp = HttpsUtil.getRealIp(exchange);
        final URI uri = exchange.getRequest().getURI();
        final String className = ex.getClass().getName();

        ResultVO<Object> result;
        if (ex instanceof CommonException exception) {
            log.warn("自定义异常捕获, realIp={}, uri={}, className={}，message={}"
                , realIp, uri, className, ex.getMessage());

            result = ResultVO.error(exception);
        } else if (ex instanceof ResponseStatusException exception) {
            log.error("响应状态异常捕获, realIp={}, uri={}, className={}，message={}"
                , realIp, uri, className, ex.getMessage(), ex);

            final HttpStatus httpStatus = HttpStatus.valueOf(exception.getStatusCode().value());
            result = ResultVO.error(httpStatus);
        } else {
            log.error("兜底异常捕获, realIp={}, uri={}, className={}，message={}"
                , realIp, uri, className, ex.getMessage(), ex);

            result = ExceptionUtil.getException(ex, className).getBody();
        }

        HttpStatusCode httpStatusCode = ExceptionUtil.getHttpStatusCode(globalErrorProperty.getEnabledStatusOk(), result.getBizStatus());

        final ResponseBodyCryptoConfig crypto = responseBodyProperty.getCrypto();
        if (Boolean.TRUE.equals(crypto.getEnabled())) {
            final String encrypt = CryptoUtil.encrypt(JacksonUtil.toJsonStr(result.getBizData()), crypto);
            result.setBizData(encrypt);
        }

        return ReactorUtil.getMonoResponse(exchange, httpStatusCode, result);
    }

}